﻿using System;
using System.Collections.Generic;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Matchingo.Common.Constants;
using Matchingo.Common.Types.Types;
using Matchingo.Models.ScoreProvider;

namespace GameLogicTestFixtureApp
{
    [TestClass]
    public class HighScoreProviderFixture
    {
        [TestMethod]
        public void ShouldLoadGameRecords()
        {
            HighScoreProvider prov = new HighScoreProvider();
            prov.LoadRecords();            
        }

        [TestMethod]
        public void ShouldLoadAndSaveGameRecords()
        {
            // create a new HighScoreProvider and add a game to it
            HighScoreProvider prov = new HighScoreProvider();
            Assert.IsTrue(prov.GameRecords.Count == 0);

            Game g = new Game()
            {
                ArtificialGame = false,
                CardCount = 10,
                StartTime = new DateTime(2008, 12, 6, 8, 30, 01),
                EndTime = new DateTime(2008, 12, 6, 8, 30, 44),
                PlayerName = "PlayerName",
                Score = 444,
                TurnCount = 14,
                GameState = Game.GameStateEnum.Ended
            };
            prov.AddRecord(g, false);
            Guid uniqueGuid = g.UniqueID;

            // make sure the game record is valid
            #region Asserts
            Assert.IsTrue(prov.GameRecords.Count == 1);
            Assert.IsTrue(prov.GameRecords[0].ArtificialGame == false);
            Assert.IsTrue(prov.GameRecords[0].CardCount == 10);
            Assert.IsTrue(prov.GameRecords[0].StartTime.Value.CompareTo(new DateTime(2008, 12, 6, 8, 30, 01)) == 0);
            Assert.IsTrue(prov.GameRecords[0].EndTime.Value.CompareTo(new DateTime(2008, 12, 6, 8, 30, 44)) == 0);
            Assert.IsTrue(prov.GameRecords[0].PlayerName == "PlayerName");
            Assert.IsTrue(prov.GameRecords[0].Score == 444);
            Assert.IsTrue(prov.GameRecords[0].TurnCount == 14);
            Assert.IsTrue(prov.GameRecords[0].UniqueID == uniqueGuid);
            Assert.IsTrue(prov.GameRecords[0].GameState == Game.GameStateEnum.Ended);
            #endregion

            // now just load the scores and make sure our single entry exists with the same values
            prov = new HighScoreProvider();
            prov.LoadRecords();

            // make sure the game record is valid
            #region Asserts
            Assert.IsTrue(prov.GameRecords.Count == 1);
            Assert.IsTrue(prov.GameRecords[0].ArtificialGame == false);
            Assert.IsTrue(prov.GameRecords[0].CardCount == 10);
            Assert.IsTrue(prov.GameRecords[0].StartTime.Value.CompareTo(new DateTime(2008, 12, 6, 8, 30, 01)) == 0);
            Assert.IsTrue(prov.GameRecords[0].EndTime.Value.CompareTo(new DateTime(2008, 12, 6, 8, 30, 44)) == 0);
            Assert.IsTrue(prov.GameRecords[0].PlayerName == "PlayerName");
            Assert.IsTrue(prov.GameRecords[0].Score == 444);
            Assert.IsTrue(prov.GameRecords[0].TurnCount == 14);
            Assert.IsTrue(prov.GameRecords[0].UniqueID == uniqueGuid);
            Assert.IsTrue(prov.GameRecords[0].GameState == Game.GameStateEnum.Ended);
            #endregion
        }

        [TestMethod]        
        public void ShouldFailAddingRecordOfGameInProgress()
        {
            // create a new HighScoreProvider and add a game to it
            HighScoreProvider prov = new HighScoreProvider();
            Assert.IsTrue(prov.GameRecords.Count == 0);

            Game g = new Game()
            {
                ArtificialGame = false,
                CardCount = 10,
                StartTime = new DateTime(2008, 12, 6, 8, 30, 01),
                EndTime = new DateTime(2008, 12, 6, 8, 30, 44),
                PlayerName = "PlayerName",
                Score = 444,
                TurnCount = 14,
                GameState = Game.GameStateEnum.Started
            };

            // should throw an exception for trying to add a record of a non-stopped game
            try
            {
                prov.AddRecord(g, false);

                // should not reach here, because an exception should've occurred
                Assert.IsTrue(false);
            }
            catch (ArgumentException) { }
            catch (Exception) { Assert.IsTrue(false); } // should not fire, should be caught in previous typed catch
        }

        [TestMethod]
        public void ShouldGetHighScoresByScore()
        {
            // add 3 fake games
            HighScoreProvider prov = new HighScoreProvider();
			prov.AddRecord(new Game() { Score = 500, CardCount = CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT, GameState = Game.GameStateEnum.Ended, ArtificialGame = false }, false);
			prov.AddRecord(new Game() { Score = 100, CardCount = CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT, GameState = Game.GameStateEnum.Ended, ArtificialGame = false }, false);
			prov.AddRecord(new Game() { Score = 200, CardCount = CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT, GameState = Game.GameStateEnum.Ended, ArtificialGame = false }, false);

            List<Game> gamesByScore = prov.GetHighScoresByCardCount(3, CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT);

            // make sure that they are ordered highest to lowest
            Assert.IsTrue(gamesByScore.Count == 3);
            Assert.IsTrue(gamesByScore[0].Score == 500);
            Assert.IsTrue(gamesByScore[1].Score == 200);
            Assert.IsTrue(gamesByScore[2].Score == 100);
        }

        [TestMethod]
        public void ShouldHandleRequestingMoreScoresThanAvailable()
        {
            // add 1 fake game
            HighScoreProvider prov = new HighScoreProvider();
            prov.AddRecord(new Game() { Score = 500, CardCount = CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT, GameState = Game.GameStateEnum.Ended, ArtificialGame = false }, false);

            // request 3 (ensure no exception occurred)
            prov.GetHighScoresByCardCount(3, CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT);
        }

        [TestMethod]
        public void ShouldThrowExceptionIfAskedToSaveArtificalGame()
        {
            HighScoreProvider prov = new HighScoreProvider();

            try
            {
				prov.AddRecord(new Game() { Score = 500, CardCount = CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT, GameState = Game.GameStateEnum.Ended }, false);

                // should not reach here, because an exception should've occurred
                Assert.IsTrue(false);
            }
            catch (ArgumentException)
            {
                // this is what we expect to catch
                return;
            }
            catch
            {
                Assert.IsTrue(false); // should not catch just any exception, but the one expected above
            }
        }

        [TestMethod]
        public void ShouldFireRecordAddedEvent()
        {
            bool eventFired = false;
            HighScoreProvider prov = new HighScoreProvider();
            prov.RecordAdded += (s, e) => { eventFired = true; };
			prov.AddRecord(new Game() { Score = 500, CardCount = CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT, GameState = Game.GameStateEnum.Ended, ArtificialGame = false }, false);

            Assert.IsTrue(eventFired);
        }
    }
}